package org.example.apifallback;



import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import org.example.apifallback.notice.CommentNotice;
import org.example.apifallback.util.AnnotationUtils;
import org.example.apifallback.util.FullNameUtils;
import org.example.apifallback.util.StrFormatter;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.util.*;

/**
 * @author zengxiaoyu
 * @date 2025/7/31 15:07
 */
public class FallbackAction extends AnAction {


    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        Editor editor = e.getData(CommonDataKeys.EDITOR);
        assert editor != null;
        VirtualFile virtualFile = editor.getVirtualFile();
        // 根据 virtualFile 获取 psiClass
        assert project != null;
        PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
        if (psiFile != null) {
            psiFile.accept(new JavaRecursiveElementVisitor() {
                @Override
                public void visitClass(@NotNull PsiClass psiClass) {
                    boolean isFeignClient = judgeFeignClient(psiClass);
                    if (isFeignClient) {
                        String directoryPath = virtualFile.getParent().getPath();
                        String fallbackPath = StrFormatter.format("{}{}{}Fallback.java", directoryPath, System.getProperty("file.separator"), psiClass.getName());
                        boolean exists = checkFileExists(project, fallbackPath);
                        if (exists) {
                            CommentNotice.show(StrFormatter.format("{}Fallback.java 文件已存在!", psiClass.getName()), project);
                        } else {
                            String fallbackText = generateFallbackClass(project, editor, virtualFile, psiClass);
                            VirtualFile parentDir = virtualFile.getParent();
                            PsiDirectory psiDirectory = PsiManager.getInstance(project).findDirectory(parentDir);
                            assert psiDirectory != null;
                            CommentNotice.show(StrFormatter.format("{}Fallback.java 文件开始生成!", psiClass.getName()), project);
                            WriteAction.run(() -> {
                                createJavaFile(project, psiDirectory, StrFormatter.format("{}Fallback.java", psiClass.getName()), fallbackText);
                                CommentNotice.show(StrFormatter.format("{}Fallback.java 文件生成成功!", psiClass.getName()), project);
                            });
                        }
                    } else {

                        StringBuilder annotationText = new StringBuilder();
//                        for (PsiAnnotation annotation : psiClass.getAnnotations()) {
//                            String qualifiedName = annotation.getQualifiedName();
//                            annotationText.append(qualifiedName).append(",");
//                        }
                        CommentNotice.show(StrFormatter.format("不是FeignClient接口, 无法生成Fallback类!"+ annotationText, psiClass.getName()), project);
                    }
                }
            });
        }

    }

    public boolean checkFileExists(Project project, String filePath) {
        // 方法1：检查物理文件
        File ioFile = new File(filePath);
        if (!ioFile.exists()) return false;

        // 方法2：检查 VirtualFile（确保在IDE内）
        VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(filePath);
        if (vFile == null) return false;

        // 方法3：检查PSI文件（可选）
        PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile);
        return psiFile != null; // 根据需求返回
    }

    private boolean judgeFeignClient(PsiClass psiClass) {
        Optional<PsiAnnotation> feignClient = AnnotationUtils.findAnnotation(psiClass, "FeignClient");
        return feignClient.isPresent();
    }


    private void createJavaFile(Project project, PsiDirectory directory, String fileName, String content) {
        PsiFileFactory factory = PsiFileFactory.getInstance(project);
        PsiFile javaFile = factory.createFileFromText(
                fileName,
                JavaFileType.INSTANCE, // 指定文件类型为Java
                content
        );
        directory.add(javaFile); // 将文件添加到目录
    }



    private String generateFallbackClass(Project project, Editor editor, VirtualFile virtualFile, PsiClass psiClass) {
        PsiClass objectClass = JavaPsiFacade.getInstance(project)
                .findClass("java.lang.Object", GlobalSearchScope.allScope(project));
        String businessName = "#业务名称#";
        businessName = autoFindBusinessName(psiClass, businessName);
        String simpleClassName = psiClass.getName();
        String qualifiedName = psiClass.getQualifiedName();
        StringBuilder fallbackText = new StringBuilder();
        fallbackText.append(StrFormatter.format("package {};\n", qualifiedName.substring(0, qualifiedName.length() - simpleClassName.length() - 1)));
        fallbackText.append("import com.linkcircle.boss.framework.common.model.CommonResult;\n");
        fallbackText.append("import org.springframework.cloud.openfeign.FallbackFactory;\n");
        fallbackText.append("import org.springframework.stereotype.Component;\n");
        fallbackText.append("import lombok.extern.slf4j.Slf4j;\n");
        fallbackText.append(StrFormatter.format("import {};\n", qualifiedName));


        Set<String> fullParamNames = new HashSet<>();
        for (PsiMethod declaredMethod : psiClass.getAllMethods()) {
            if(declaredMethod.getContainingClass()!=null && declaredMethod.getContainingClass().equals(objectClass)){
                continue;
            }
            PsiParameter[] parameters = declaredMethod.getParameterList().getParameters();

            if (parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    String type = parameters[i].getType().getCanonicalText();
                    fullParamNames.addAll(FullNameUtils.fullNames(type));
                }
            }
            PsiType returnType = declaredMethod.getReturnType();
            if(returnType!=null){
                fullParamNames.addAll(FullNameUtils.fullNames(returnType.getCanonicalText()));
            }
        }
        if(!fullParamNames.isEmpty()){
            for (String fullParamName : fullParamNames) {
                fallbackText.append(StrFormatter.format("import {};\n", fullParamName));
            }
        }
        fallbackText.append("@Component\n");
        fallbackText.append("@Slf4j\n");
        fallbackText.append(StrFormatter.format("public class {}Fallback  implements FallbackFactory<{}> {\n", simpleClassName, simpleClassName));
        fallbackText.append("    @Override\n");
        fallbackText.append(StrFormatter.format("    public {} create(Throwable cause) {\n", simpleClassName));
        fallbackText.append(StrFormatter.format("        return new {}() {\n", simpleClassName));
        for (PsiMethod declaredMethod : psiClass.getAllMethods()) {
            if(declaredMethod.getContainingClass()!=null && declaredMethod.getContainingClass().equals(objectClass)){
                continue;
            }
            fallbackText.append(StrFormatter.format("            @Override\n"));

            PsiParameter[] parameters = declaredMethod.getParameterList().getParameters();
            String formatMsg = "            public {} {}(";
            String logMsg = "                log.error(\"调用" + businessName + "API失败，";
            List<String> paramNames = new ArrayList<>();
            if (parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    String name = parameters[i].getName();
                    String type = parameters[i].getType().getCanonicalText();
                    formatMsg += StrFormatter.format("{} {},", FullNameUtils.deepReplace(type), name);
                    logMsg += StrFormatter.format("{}: {},", name, "{}");
                    paramNames.add(name);
                }
                if (formatMsg.endsWith(",")) {
                    formatMsg = formatMsg.substring(0, formatMsg.length() - 1);
                }
                if (logMsg.endsWith(",")) {
                    logMsg = logMsg.substring(0, logMsg.length() - 1);
                }
            }
            PsiType returnType = declaredMethod.getReturnType();
            String methodReturnType = returnType==null? "Void" : returnType.getCanonicalText();
            fallbackText.append(StrFormatter.format(formatMsg + "){\n", FullNameUtils.deepReplace(methodReturnType), declaredMethod.getName()));
            if(paramNames.isEmpty()){
                fallbackText.append(logMsg).append(" 异常信息: \" ").append(", cause);\n");
            }else{
                fallbackText.append(logMsg).append(" 异常信息: \", ").append(String.join(",", paramNames)).append(", cause);\n");
            }
            fallbackText.append("                return CommonResult.error(500, \"调用失败: \" + cause.getMessage());\n");
            fallbackText.append("            }\n");
        }
        fallbackText.append("      };\n");
        fallbackText.append("  }\n");
        fallbackText.append("}\n");

        return fallbackText.toString();
    }




    private String autoFindBusinessName(PsiClass psiClass, String businessName) {
        Optional<PsiAnnotation> apiAnnotationOpt =  AnnotationUtils.findAnnotation(psiClass, "Tag");
        if (apiAnnotationOpt.isPresent()) {
            PsiAnnotationMemberValue name = apiAnnotationOpt.get().findAttributeValue("name");
            if (name != null && Objects.nonNull(name.getText()) && !name.getText().trim().isEmpty()) {
                businessName = name.getText().replace("\"", "").replace(" ", "");
            } else {
                PsiAnnotationMemberValue description = apiAnnotationOpt.get().findAttributeValue("description");
                if (description != null && Objects.nonNull(description.getText()) && !description.getText().trim().isEmpty()) {
                    businessName = description.getText().replace("\"", "").replace(" ", "");
                }
            }
        }
        return businessName;
    }

}
